package com.liliudong.template.biz.service.sys.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.crypto.digest.DigestUtil;
import com.liliudong.template.biz.mapper.sys.SysUserMapper;
import com.liliudong.template.biz.service.sys.SysMenuService;
import com.liliudong.template.biz.service.sys.SysRoleService;
import com.liliudong.template.biz.service.sys.SysUserRoleService;
import com.liliudong.template.biz.service.sys.SysUserService;
import com.liliudong.template.common.constant.SaSessionConst;
import com.liliudong.template.common.constant.SecurityConst;
import com.liliudong.template.common.convert.sys.SysUserConvert;
import com.liliudong.template.common.request.sys.SysUserAddRequest;
import com.liliudong.template.common.request.sys.SysUserModifyRequest;
import com.liliudong.template.common.entity.sys.SysRole;
import com.liliudong.template.common.entity.sys.SysUser;
import com.liliudong.template.common.exception.BizException;
import com.liliudong.template.common.response.sys.SysUserDetailResponse;
import com.liliudong.template.common.security.CurrentUser;
import com.liliudong.template.common.security.util.SecurityUtil;
import com.liliudong.template.common.response.sys.SysUserLoginResponse;
import com.liliudong.template.common.response.sys.SysUserPageResponse;
import com.liliudong.template.common.response.sys.SysUserProfileResponse;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.liliudong.template.common.entity.sys.table.SysDeptTableDef.SYS_DEPT;
import static com.liliudong.template.common.entity.sys.table.SysUserTableDef.SYS_USER;

/**
 * @author liliudong
 */
@Service
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    private static final String ERROR_USERNAME_OR_PASSWORD = "用户名或密码不正确";

    private final SysRoleService sysRoleService;

    private final SysMenuService sysMenuService;

    private final SysUserRoleService sysUserRoleService;

    @Override
    public SysUserLoginResponse login(String username, String password) {
        SysUser sysUser = getOneByUsernameIgnoreDataPermission(username);
        if (Objects.isNull(sysUser)) {
            throw new BizException(ERROR_USERNAME_OR_PASSWORD);
        }
        if (!DigestUtil.bcryptCheck(password, sysUser.getPassword())) {
            throw new BizException(ERROR_USERNAME_OR_PASSWORD);
        }
        // 登录
        StpUtil.login(sysUser.getId());
        // 缓存登录账户信息
        List<SysRole> sysRoles = sysRoleService.listByUserIdIgnoreDataPermission(sysUser.getId());
        Set<Long> roleIds = sysRoles.stream()
                .map(SysRole::getId)
                .collect(Collectors.toSet());
        Set<String> rolePermissions = sysRoles.stream()
                .map(SysRole::getPermission)
                .collect(Collectors.toSet());
        // 是否是超级管理员
        boolean isAdmin = rolePermissions.contains(SecurityConst.SUPER_ADMIN_ROLE_PERMISSION);
        Set<String> permissions = sysMenuService.listPermissionByRoleIdsIgnoreDataPermission(roleIds, isAdmin);
        Set<SysRole.DataScope> dataScopes = sysRoleService.listDataScopeByUserIdIgnoreDataPermission(sysUser.getId(), isAdmin);
        cacheUserProfile(CurrentUser.of(sysUser), dataScopes, permissions, rolePermissions);
        // 返回登录信息
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        long expiresTime = System.currentTimeMillis() + tokenInfo.getTokenTimeout();

        SysUserLoginResponse sysUserLoginResponse = new SysUserLoginResponse();
        sysUserLoginResponse.setAvatar(sysUser.getAvatar());
        sysUserLoginResponse.setUsername(sysUser.getAvatar());
        sysUserLoginResponse.setNickname(sysUser.getNickname());
        sysUserLoginResponse.setRoles(rolePermissions);
        sysUserLoginResponse.setPermissions(permissions);
        sysUserLoginResponse.setAccessToken(tokenInfo.getTokenValue());
        sysUserLoginResponse.setExpires(expiresTime);
        return sysUserLoginResponse;
    }

    /**
     * 按用户名获取忽略数据权限
     *
     * @param username 用户名
     * @return {@link SysUser }
     */
    private SysUser getOneByUsernameIgnoreDataPermission(String username) {
        return queryChainIgnoreDataPermission()
                .where(SYS_USER.USERNAME.eq(username))
                .one();
    }

    @Override
    public SysUserProfileResponse profile() {
        SysUserProfileResponse sysUserProfileResponse = new SysUserProfileResponse();

        long userId = StpUtil.getLoginIdAsLong();
        // 获取用户信息
        SysUser sysUser = this.getByIdIgnoreDataPermission(userId);
        // 获取用户角色信息
        List<SysRole> sysRoles = sysRoleService.listByUserIdIgnoreDataPermission(userId);
        Set<Long> roleIds = sysRoles.stream()
                .map(SysRole::getId)
                .collect(Collectors.toSet());
        Set<String> rolePermissions = sysRoles.stream()
                .map(SysRole::getPermission)
                .collect(Collectors.toSet());
        // 是否是超级管理员
        boolean isAdmin = rolePermissions.contains(SecurityConst.SUPER_ADMIN_ROLE_PERMISSION);
        Set<String> permissions = sysMenuService.listPermissionByRoleIdsIgnoreDataPermission(roleIds, isAdmin);
        Set<SysRole.DataScope> dataScopes = sysRoleService.listDataScopeByUserIdIgnoreDataPermission(userId, isAdmin);
        // 刷新缓存登录账户信息
        cacheUserProfile(CurrentUser.of(sysUser), dataScopes, permissions, rolePermissions);

        List<Tree<Long>> trees = sysMenuService.treeByRoleIdsIgnoreDataPermission(roleIds, isAdmin);

        sysUserProfileResponse.setUsername(sysUser.getUsername());
        sysUserProfileResponse.setRolePermissions(SecurityUtil.getRoleList());
        sysUserProfileResponse.setPermissions(SecurityUtil.getPermissionList());
        sysUserProfileResponse.setMenuTree(trees);
        sysUserProfileResponse.setId(userId);
        return sysUserProfileResponse;
    }

    @Override
    @Transactional
    public void addOne(SysUserAddRequest sysUserAddRequest) {
        Set<Long> roleIds = sysUserAddRequest.getRoleIds();
        SysUser sysUser = SysUserConvert.INSTANCE.addDto2Entity(sysUserAddRequest);
        // 生成用户密码
        String password = DigestUtil.bcrypt(SecurityConst.DEFAULT_PASSWORD);
        sysUser.setPassword(password);
        // 保存用户
        this.save(sysUser);
        // 保存用户和角色关系
        sysUserRoleService.saveBatchWithUserId(sysUser.getId(), roleIds);
    }

    @Override
    public Page<SysUserPageResponse> queryPage(Long page, Long size) {
        return queryChain()
                .select(SYS_USER.DEFAULT_COLUMNS, SYS_DEPT.NAME.as(SysUserPageResponse.Fields.deptName))
                .leftJoin(SYS_DEPT)
                .on(SYS_USER.DEPT_NO.eq(SYS_DEPT.DEPT_NO))
                .pageAs(Page.of(page, size), SysUserPageResponse.class);
    }

    @Override
    @Transactional
    public void modifyById(Long id, SysUserModifyRequest sysUserModifyRequest) {
        Set<Long> roleIds = sysUserModifyRequest.getRoleIds();
        SysUser sysUser = SysUserConvert.INSTANCE.modifyDto2Entity(sysUserModifyRequest);
        // 保存用户
        sysUser.setId(id);
        this.updateById(sysUser);
        // 删除用户和角色关系
        sysUserRoleService.removeByUserId(id);
        // 保存用户和角色关系
        sysUserRoleService.saveBatchWithUserId(sysUser.getId(), roleIds);
    }

    @Override
    public SysUserDetailResponse detailById(Long id) {
        SysUserDetailResponse sysUserDetailVO = this.getOneAs(QueryWrapper.create().where(SYS_USER.ID.eq(id)), SysUserDetailResponse.class);
        // 获取角色ID信息
        List<Long> roleIds = sysUserRoleService.listRoleIdsByUserId(id);
        sysUserDetailVO.setRoleIds(roleIds);
        return sysUserDetailVO;
    }

    /**
     * 按id获取忽略数据权限
     *
     * @param userId 用户id
     * @return {@link SysUser }
     */
    private SysUser getByIdIgnoreDataPermission(long userId) {
        return queryChainIgnoreDataPermission()
                .where(SYS_USER.ID.eq(userId))
                .one();
    }

    /**
     * 缓存用户信息
     *
     * @param currentUser     当前用户
     * @param dataScopes      数据范围
     * @param permissions     权限
     * @param rolePermissions 角色权限
     */
    private void cacheUserProfile(CurrentUser currentUser,
                                  Set<SysRole.DataScope> dataScopes,
                                  Set<String> permissions,
                                  Set<String> rolePermissions) {
        StpUtil.getTokenSession()
                .set(SaSessionConst.DATA_SCOPES, dataScopes)
                .set(SaSessionConst.DEPT_NO, currentUser.getDeptNo())
                .set(SaSession.USER, currentUser)
                .set(SaSession.PERMISSION_LIST, permissions)
                .set(SaSession.ROLE_LIST, rolePermissions);
    }
}
